// Implementation of the Socket class.
/*  
    int s;
    int optval;
    socklen_t optlen = sizeof (optval);
    int option = 1;
    int keepalive_intvl = 1;
    int keepalive_count = 1;
    int keepalive_idle = 1;

    setsockopt(the_socket, SOL_SOCKET, SO_KEEPALIVE, &option, sizeof (int));
    setsockopt(the_socket, SOL_TCP, TCP_KEEPINTVL, &keepalive_intvl, sizeof (int));
    setsockopt(the_socket, SOL_TCP, TCP_KEEPCNT, &keepalive_count, sizeof (int));
    setsockopt(the_socket, SOL_TCP, TCP_KEEPIDLE, &keepalive_idle, sizeof (int));
*/
 
/*
//Create the socket 
    if ((s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
        perror("socket()");
        exit(EXIT_FAILURE);
    }

    //Set the option active 
    optval = 1;
    optlen = sizeof (optval);
    if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) {
        perror("setsockopt()");
        close(s);
        exit(EXIT_FAILURE);
    }

    printf("SO_KEEPALIVE set on socket\n");

    //Check the status again 
    if (getsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &optval, &optlen) < 0) {
        perror("getsockopt()");
        close(s);
        exit(EXIT_FAILURE);
    }
    printf("SO_KEEPALIVE is %s\n", (optval ? "ON" : "OFF"));
     
   // close(s);
   // exit(EXIT_SUCCESS);
 
*/


#include "Socket.h"
#include "string.h"
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <iostream>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>

MySocket::MySocket() :
m_sock(-1) {

    memset(&m_addr,
            0,
            sizeof ( m_addr));

}

MySocket::~MySocket() {
    if (is_valid())
        ::close(m_sock);
}

bool MySocket::create() {
    m_sock = socket(AF_INET, SOCK_STREAM, 0);
    int s;
    int optval;
    socklen_t optlen = sizeof (optval);

    if (!is_valid())
        return false;

    optval = 1;
    optlen = sizeof (optval);

    if (setsockopt(m_sock, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) {
        perror("setsockopt()");
        close(m_sock);
    }

    if (getsockopt(m_sock, SOL_SOCKET, SO_KEEPALIVE, &optval, &optlen) < 0) {
        perror("getsockopt()");
        close(m_sock);
    }

    cout << "SO_KEEPALIVE is %s\n" << (optval ? "ON" : "OFF") << endl;

    // TIME_WAIT - argh
    int on = 1;
    if (setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, (const char*) &on, sizeof ( on)) == -1)
        return false;


    return true;

}

bool MySocket::bind(const int port) {

    if (!is_valid()) {
        return false;
    }



    m_addr.sin_family = AF_INET;
    m_addr.sin_addr.s_addr = INADDR_ANY;
    m_addr.sin_port = htons(port);

    int bind_return = ::bind(m_sock,
            (struct sockaddr *) &m_addr,
            sizeof ( m_addr));


    if (bind_return == -1) {
        return false;
    }

    return true;
}

bool MySocket::listen() const {
    if (!is_valid()) {
        return false;
    }

    int listen_return = ::listen(m_sock, MAXCONNECTIONS);


    if (listen_return == -1) {
        return false;
    }

    return true;
}

bool MySocket::accept(MySocket& new_socket) const {
    int addr_length = sizeof ( m_addr);
    new_socket.m_sock = ::accept(m_sock, (sockaddr *) & m_addr, (socklen_t *) & addr_length);

    if (new_socket.m_sock <= 0)
        return false;
    else
        return true;
}

bool MySocket::send(const std::string s) const {
    int status = ::send(m_sock, s.c_str(), s.size(), MSG_NOSIGNAL);
    if (status == -1) {
        return false;
    } else {
        return true;
    }
}

int MySocket::recv(std::string& s) const {
    char buf [ MAXRECV + 1 ];

    s = "";

    memset(buf, 0, MAXRECV + 1);

    int status = ::recv(m_sock, buf, MAXRECV, 0);

    if (status == -1) {
        cout << "status == -1   errno == " << errno << "  in Socket::recv\n";
        return 0;
    } else if (status == 0) {
        return 0;
    } else {
        s = buf;
        return status;
    }
}

bool MySocket::connect(const std::string host, const int port) {
    if (!is_valid()) return false;

    m_addr.sin_family = AF_INET;
    m_addr.sin_port = htons(port);

    int status = inet_pton(AF_INET, host.c_str(), &m_addr.sin_addr);

    if (errno == EAFNOSUPPORT) return false;

    status = ::connect(m_sock, (sockaddr *) & m_addr, sizeof ( m_addr));

    if (status == 0)
        return true;
    else
        return false;
}

void MySocket::set_non_blocking(const bool b) {

    int opts;

    opts = fcntl(m_sock,
            F_GETFL);

    if (opts < 0) {
        return;
    }

    if (b)
        opts = (opts | O_NONBLOCK);
    else
        opts = (opts & ~O_NONBLOCK);

    fcntl(m_sock,
            F_SETFL, opts);

}
